/*
 * Copyright (c) 2010, Michael Hengler (developer (at) hengler (dot) de),
 * Nikolaus Moll (developer (at) miblounge (dot) net).
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * * Neither the name of the Jombi project nor the
 * names of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL JOMBI BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
package net.miblounge.jombi.impl.widgets;

import java.util.List;

import org.jowidgets.common.widgets.controller.IMenuListener;
import org.jowidgets.spi.impl.controller.MenuObservable;
import org.jowidgets.spi.widgets.IActionMenuItemSpi;
import org.jowidgets.spi.widgets.IMenuItemSpi;
import org.jowidgets.spi.widgets.IMenuSpi;
import org.jowidgets.spi.widgets.ISelectableMenuItemSpi;
import org.jowidgets.spi.widgets.ISubMenuSpi;

import com.trolltech.qt.core.QTimer;
import com.trolltech.qt.gui.QAction;
import com.trolltech.qt.gui.QActionGroup;
import com.trolltech.qt.gui.QCursor;
import com.trolltech.qt.gui.QMenu;
import com.trolltech.qt.gui.QToolTip;

public class QtMenu implements IMenuSpi {

	private static final int TOOLTIP_DELAY_IN_MS = 400;

	private final QMenu menu;

	private final MenuObservable menuObservable;

	private QAction lastAction;
	private QTimer timer;

	public QtMenu(final QMenu menu) {
		this.menu = menu;
		this.menuObservable = new MenuObservable();

		menu.hovered.connect(this, "hover(QAction)");
		menu.aboutToHide.connect(this, "menuHide()");
		menu.aboutToShow.connect(this, "menuShow()");
	}

	protected void menuHide() {
		QToolTip.hideText();
		lastAction = null;
		menuObservable.fireMenuDeactivated();
	}

	protected void menuShow() {
		menuObservable.fireMenuActivated();
	}

	@Override
	public QMenu getUiReference() {
		return menu;
	}

	@Override
	public void remove(final int index) {
		menu.actions().remove(index);

		//		final QAction item = menu.actions().get(index);
		//		if (item != null) {
		//			// TODO: improve
		//			item.dispose();
		//		}
	}

	@Override
	public void setEnabled(final boolean enabled) {
		menu.setEnabled(enabled);
	}

	@Override
	public boolean isEnabled() {
		return menu.isEnabled();
	}

	protected void addMenuItem(final QAction menuItem, final Integer index) {
		if (addToEnd(index, menu)) {
			menu.addAction(menuItem);
		} else {
			final QAction before = menu.actions().get(index);
			menu.insertAction(before, menuItem);
		}
	}

	private ActionMenuItemImpl createActionItem(final QAction menuItem) {
		final ActionMenuItemImpl result = new ActionMenuItemImpl(menuItem);

		return result;
	}

	protected void hover(final QAction action) {
		final boolean wasVisible = QToolTip.isVisible();
		QToolTip.hideText();

		final String toolTipText = action.toolTip();
		final boolean hasToolTip = toolTipText != null
			&& !toolTipText.isEmpty()
			&& !toolTipText.equals(action.text().replace("&", ""));

		if (wasVisible) {
			if (hasToolTip) {
				showToolTip(toolTipText);
			}
		} else if (hasToolTip) {

			if (timer != null) {
				timer.stop();
				timer.dispose();
				timer = null;
			}

			lastAction = action;
			timer = new QTimer(getUiReference());
			timer.timeout.connect(this, "toolTipTimer()");
			timer.start(TOOLTIP_DELAY_IN_MS);
		}
	}

	protected void toolTipTimer() {
		// do not dispose in own event handler: timer.dispose();
		timer = null;

		if (lastAction != null) {
			showToolTip(lastAction.toolTip());
		}
	}

	private void showToolTip(final String message) {
		QToolTip.showText(QCursor.pos(), message);
	}

	protected QActionGroup getActionGroup(final QAction action) {
		final List<QAction> actions = menu.actions();
		final int pos = actions.indexOf(action);

		int index = pos - 1;
		while (index >= 0) {
			final QAction current = actions.get(index);
			if (current.isSeparator()) {
				break;
			}

			if (current.actionGroup() != null) {
				return current.actionGroup();
			}

			index--;
		}

		index = pos + 1;
		while (index < actions.size()) {
			final QAction current = actions.get(index);
			if (current.isSeparator()) {
				break;
			}

			if (current.actionGroup() != null) {
				return current.actionGroup();
			}

			index++;
		}

		return new QActionGroup(menu);
	}

	@Override
	public IActionMenuItemSpi addActionItem(final Integer index) {
		final QAction menuItem = new QAction(menu);

		addMenuItem(menuItem, index);

		return createActionItem(menuItem);
	}

	@Override
	public ISelectableMenuItemSpi addCheckedItem(final Integer index) {
		final QAction menuItem = new QAction(menu);

		addMenuItem(menuItem, index);

		menuItem.setCheckable(true);

		return createSelectableItem(menuItem);
	}

	@Override
	public ISelectableMenuItemSpi addRadioItem(final Integer index) {
		final QAction menuItem = new QAction(menu);

		addMenuItem(menuItem, index);

		menuItem.setActionGroup(getActionGroup(menuItem));

		menuItem.setCheckable(true);

		return createSelectableItem(menuItem);
	}

	@Override
	public IMenuItemSpi addSeparator(final Integer index) {
		QAction menuItem = null;

		if (addToEnd(index, menu)) {
			menuItem = menu.addSeparator();
		} else {
			final QAction before = menu.actions().get(index);
			menuItem = menu.insertSeparator(before);
		}

		return createActionItem(menuItem);
	}

	private SelectableMenuItemImpl createSelectableItem(final QAction menuItem) {
		final SelectableMenuItemImpl result = new SelectableMenuItemImpl(menuItem);
		//installTooltip(menuItem, result);
		return result;
	}

	protected static boolean addToEnd(final Integer index, final QMenu menu) {
		return (index == null) || (index.intValue() == menu.actions().size());
	}

	@Override
	public ISubMenuSpi addSubMenu(final Integer index) {
		final QMenu subMenu = new QMenu(menu);
		QAction subMenuItem = null;

		if (addToEnd(index, menu)) {
			subMenuItem = menu.addMenu(subMenu);
		} else {
			final List<QAction> actions = menu.actions();
			final QAction before = actions.get(index);

			subMenuItem = menu.insertMenu(before, subMenu);
		}
		subMenuItem.setToolTip("");

		return createSubMenu(subMenuItem, subMenu);
	}

	private SubMenuImpl createSubMenu(final QAction subMenuItem, final QMenu subMenu) {
		final SubMenuImpl result = new SubMenuImpl(subMenuItem, subMenu);
		return result;
	}

	@Override
	public void addMenuListener(final IMenuListener listener) {
		menuObservable.addMenuListener(listener);
	}

	@Override
	public void removeMenuListener(final IMenuListener listener) {
		menuObservable.removeMenuListener(listener);
	}
}
